package com.happy.unitconvert.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.happy.unitconvert.common.exception.BusinessException;
import com.happy.unitconvert.config.properties.AppProperties;
import com.happy.unitconvert.model.dto.ExchangeRateResponse;
import com.happy.unitconvert.service.ExchangeRateApiService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

/**
 * 汇率API服务实现
 * 集成第三方汇率API服务
 *
 * @author Happy
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ExchangeRateApiServiceImpl implements ExchangeRateApiService {

  private final WebClient webClient;
  private final AppProperties appProperties;
  private final ObjectMapper objectMapper;

  // 支持的货币代码
  private static final List<String> SUPPORTED_CURRENCIES = Arrays.asList(
      "USD", "EUR", "GBP", "JPY", "CNY", "KRW", "HKD", "TWD", "SGD", "AUD",
      "CAD", "CHF", "SEK", "NOK", "DKK", "PLN", "CZK", "HUF", "RUB", "BRL",
      "MXN", "INR", "THB", "IDR", "MYR", "PHP", "VND", "NZD", "ZAR", "TRY");

  @Override
  public Mono<ExchangeRateResponse> fetchLatestRates(String baseCurrency) {
    log.info("开始获取汇率数据，基础货币: {}", baseCurrency);

    String url = buildApiUrl(baseCurrency);
    AppProperties.ExchangeRate config = appProperties.getExchangeRate();

    return webClient
        .get()
        .uri(url)
        .retrieve()
        .bodyToMono(String.class)
        .timeout(Duration.ofSeconds(config.getTimeoutSeconds()))
        .retryWhen(Retry.backoff(config.getRetryCount(), Duration.ofSeconds(1))
            .filter(this::isRetryableException))
        .map(this::parseExchangeRateResponse)
        .doOnSuccess(response -> {
          if (response.isSuccess()) {
            log.info("成功获取汇率数据，基础货币: {}，包含{}种货币汇率",
                baseCurrency, response.getRates().size());
          } else {
            log.error("API返回错误，基础货币: {}，错误类型: {}，错误信息: {}",
                baseCurrency, response.getErrorType(), response.getErrorInfo());
          }
        })
        .doOnError(error -> log.error("获取汇率数据失败，基础货币: {}，错误: {}",
            baseCurrency, error.getMessage()))
        .onErrorMap(this::mapToBusinessException);
  }

  @Override
  public Mono<List<String>> fetchSupportedCurrencies() {
    log.info("获取支持的货币代码列表");
    // 直接返回支持的货币列表，避免额外的API调用
    return Mono.just(SUPPORTED_CURRENCIES)
        .doOnSuccess(currencies -> log.info("返回{}种支持的货币", currencies.size()));
  }

  @Override
  public Mono<Boolean> checkApiHealth() {
    log.info("检查汇率API健康状态");

    return fetchLatestRates(appProperties.getExchangeRate().getDefaultBaseCurrency())
        .map(response -> response.isSuccess())
        .doOnSuccess(isHealthy -> log.info("API健康状态检查结果: {}", isHealthy ? "健康" : "不健康"))
        .onErrorReturn(false);
  }

  @Override
  public ExchangeRateResponse parseExchangeRateResponse(String responseBody) {
    try {
      ExchangeRateResponse response = objectMapper.readValue(responseBody, ExchangeRateResponse.class);
      response.setCreatedAt(LocalDateTime.now());
      return response;
    } catch (JsonProcessingException e) {
      log.error("解析汇率API响应失败: {}", e.getMessage());
      throw new BusinessException("解析汇率数据失败: " + e.getMessage());
    }
  }

  /**
   * 构建API请求URL
   */
  private String buildApiUrl(String baseCurrency) {
    AppProperties.ExchangeRate config = appProperties.getExchangeRate();
    String baseUrl = config.getBaseUrl();

    // 确保URL格式正确
    if (!baseUrl.endsWith("/")) {
      baseUrl += "/";
    }

    return baseUrl + "latest/" + baseCurrency.toUpperCase();
  }

  /**
   * 判断异常是否可重试
   */
  private boolean isRetryableException(Throwable throwable) {
    if (throwable instanceof WebClientResponseException) {
      WebClientResponseException ex = (WebClientResponseException) throwable;
      HttpStatus status = HttpStatus.resolve(ex.getStatusCode().value());
      // 5xx 服务器错误和部分4xx错误可以重试
      return status != null && (status.is5xxServerError() ||
          status == HttpStatus.TOO_MANY_REQUESTS ||
          status == HttpStatus.REQUEST_TIMEOUT);
    }
    // 网络超时等异常也可以重试
    return throwable instanceof java.net.SocketTimeoutException ||
        throwable instanceof java.util.concurrent.TimeoutException;
  }

  /**
   * 将异常映射为业务异常
   */
  private BusinessException mapToBusinessException(Throwable throwable) {
    if (throwable instanceof BusinessException) {
      return (BusinessException) throwable;
    }

    if (throwable instanceof WebClientResponseException) {
      WebClientResponseException ex = (WebClientResponseException) throwable;
      return new BusinessException("汇率API请求失败: " + ex.getStatusCode() + " - " + ex.getResponseBodyAsString());
    }

    return new BusinessException("获取汇率数据失败: " + throwable.getMessage(), throwable);
  }
}
